package ginparse

import (
	"Hypnos-Soundcore/components/config"
	"Hypnos-Soundcore/components/encrypt"
	"Hypnos-Soundcore/components/helper"
	"Hypnos-Soundcore/constants"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/pquerna/ffjson/ffjson"
	"io/ioutil"
	"reflect"
	"strconv"
	"strings"
)

// var .
var (
	ErrEmptyBody = errors.New("body is empty")
)

// ParsePresetHeaders 从Header中解析数据
func ParsePresetHeaders(c *gin.Context, obj interface{}) {
	val := reflect.ValueOf(obj)
	s := val.Elem()
	if s.Kind() == reflect.Struct {
		for _, field := range constants.PresetHeaders {
			if value := c.GetHeader(field.HeaderKey); len(value) > 0 {
				if f := s.FieldByName(field.HeaderField); f.IsValid() && f.CanSet() {
					switch f.Kind() {
					case reflect.String:
						f.SetString(value)
					default:
					}
				}
			}
		}
	}
}

// ParseRequest parse request param
func ParseRequest(c *gin.Context, obj interface{}) (err error) {
	if c.Request.Body == nil {
		// do nothing...
		// err = ErrEmptyBody
	} else if body, readErr := ioutil.ReadAll(c.Request.Body); len(body) > 0 && readErr == nil {
		if body, err = descryptBody(c, body); err != nil {
			return
		}
		jsonErr := ffjson.Unmarshal(body, obj)
		err = jsonErr
	} else {
		err = readErr
	}
	ParsePresetHeaders(c, obj)
	return err
}

// ParseSimpleForm parse one form field
func ParseSimpleForm(c *gin.Context, field string, obj interface{}) (err error) {
	if form := c.PostForm(field); len(form) > 0 {
		var body []byte
		if body, err = descryptBody(c, []byte(form)); err != nil {
			return
		}
		err = ffjson.Unmarshal(body, obj)
	}
	ParsePresetHeaders(c, obj)
	return err
}

// ParseRequestForm parse request form
func ParseRequestForm(c *gin.Context, key string, obj interface{}) (err error) {
	if body := c.PostForm(key); len(body) >= 0 {
		tmpBody := []byte(body)
		if tmpBody, err = descryptBody(c, tmpBody); err != nil {
			return
		}
		err = ffjson.Unmarshal(tmpBody, obj)
	}
	ParsePresetHeaders(c, obj)
	return err
}

// 解密body数据
func descryptBody(c *gin.Context, body []byte) (ret []byte, err error) {
	ret = body
	if config.IsAesEncrypt() {
		ranNum, _ := helper.StringToInt(c.GetHeader(constants.HeaderRandomNumber), 0)
		if ranNum > 0 {
			key := encrypt.BuildCipherWithNum(ranNum)
			var plainText string
			if plainText, err = encrypt.AesDecrypt(key, string(body)); err != nil {
				return
			}
			ret = []byte(plainText)
		}
	}
	return
}

// RequestField request field
type RequestField struct {
	NameOrg   string
	NameSpec  string
	Value     string
	Required  bool
	In        string
	Kind      reflect.Kind
	Default   string
	SqlInject bool
}

// GetName get name
func (f RequestField) GetName() (ret string) {
	if len(f.NameSpec) > 0 {
		ret = strings.ToLower(f.NameSpec)
	} else {
		ret = strings.ToLower(f.NameOrg)
	}
	return ret
}

// GetValue get value
func (f RequestField) GetValue() (ret string) {
	if len(f.Value) > 0 {
		ret = f.Value
	} else {
		ret = f.Default
	}
	return ret
}

// GetValueAny get value
func (f RequestField) GetValueAny() (ret interface{}) {
	var value string
	if len(f.Value) > 0 {
		value = f.Value
	} else {
		value = f.Default
	}
	if f.Kind == reflect.Int {
		curDef, _ := helper.StringToInt(f.Default, 0)
		ret, _ = helper.StringToInt(value, curDef)
	} else if f.Kind == reflect.Int64 {
		curDef, _ := helper.StringToInt64(f.Default, 0)
		ret, _ = helper.StringToInt64(value, curDef)
	} else {
		ret = value
	}
	return ret
}

// GetDefaultValueAny get default value
func (f RequestField) GetDefaultValueAny() (ret interface{}) {
	if len(f.Default) > 0 {
		switch f.Kind {
		case reflect.Int:
			ret, _ = helper.StringToInt(f.Default, 0)
		case reflect.Int64:
			ret, _ = helper.StringToInt64(f.Default, 0)
		default:
			ret = f.Default
		}
	}
	return
}

// ParseAndCheckQuery parse and check query param
func ParseAndCheckQuery(c *gin.Context, obj interface{}) (err error) {
	queryFields, parseErr := parseObjFields(obj, true, false)
	if parseErr == nil && len(queryFields) > 0 {
		valueOf := reflect.ValueOf(obj)
		if valueOf.Kind() == reflect.Ptr {
			valueOf = valueOf.Elem()
		}
		for _, field := range queryFields {
			name := field.GetName()
			switch field.In {
			case "form":
				field.Value = c.PostForm(name)
				break
			case "param":
				field.Value = c.Param(name)
				break
			case "query":
			default:
				field.Value = c.Query(name)
				break
			}
			if field.Required && len(field.Value) == 0 {
				err = errors.New(name + " required")
				break
			}
			valueOf.FieldByName(field.NameOrg).Set(reflect.ValueOf(field.GetValueAny()))
		}
	}
	ParsePresetHeaders(c, obj)
	return
}

func parseQueryField(struField reflect.StructField) (ret RequestField) {
	if struField.Type.Kind() == reflect.Struct {
		return
	}
	ret.Kind = struField.Type.Kind()
	tag := struField.Tag
	ret.NameOrg = struField.Name
	// json:"name,omitempty"
	json := tag.Get("json")
	jarr := strings.Split(json, ",")
	for _, value := range jarr {
		if len(value) > 0 && value != "omitempty" {
			ret.NameSpec = value
		}
	}
	// anker:"required,default,in(query/param/form),sqlinject"
	anker := tag.Get("anker")
	aarr := strings.Split(anker, ",")
	for k, v := range aarr {
		switch k {
		case 0:
			ret.Required, _ = strconv.ParseBool(v)
			break
		case 1:
			ret.Default = v
			break
		case 2:
			ret.In = v
			break
		case 3:
			ret.SqlInject, _ = strconv.ParseBool(v)
			break
		default:
			break
		}
	}
	return ret
}

func parseRequestField(struField reflect.StructField, valuField reflect.Value) (ret RequestField) {
	ret = parseQueryField(struField)
	switch valuField.Kind() {
	case reflect.Int:
		ret.Value = fmt.Sprintf("%d", valuField.Int())
	case reflect.Int64:
		ret.Value = fmt.Sprintf("%d", valuField.Int())
	case reflect.Slice:
		if valuLen := valuField.Len(); valuLen > 0 {
			ret.Value = fmt.Sprintf("%d", valuLen)
		}
	default:
		ret.Value = valuField.String()
	}
	return ret
}

// CheckRequest check param required
func CheckRequest(obj interface{}) (err error) {
	parseFields, parseErr := parseObjFields(obj, true, true)
	if parseErr == nil && len(parseFields) > 0 {
		for _, field := range parseFields {
			if field.Required && len(field.Value) == 0 {
				text := fmt.Sprintf(constants.GetTextByCodeDefault(constants.TemplateFieldRequired), field.GetName())
				err = errors.New(text)
				break
			} else if field.SqlInject && len(field.Value) > 0 && HasSQLInject(field.Value) {
				text := fmt.Sprintf(constants.GetTextByCodeDefault(constants.TemplateFieldRequired), field.GetName())
				err = errors.New(text)
				break
			}
		}
	}
	return err
}

// parseObjFields parse obj fields
func parseObjFields(obj interface{}, recur bool, withValue bool) (fields []RequestField, err error) {
	typeOf := reflect.TypeOf(obj)
	valueOf := reflect.ValueOf(obj)
	if valueOf.Kind() == reflect.Ptr {
		typeOf = typeOf.Elem()
		valueOf = valueOf.Elem()
	}
	if valueOf.Kind() == reflect.Slice {
		return
	}
	for i := 0; i < valueOf.NumField(); i++ {
		if recur && valueOf.Field(i).Type().Kind() == reflect.Struct {
			recurFields, recurErr := parseObjFields(valueOf.Field(i).Interface(), recur, withValue)
			if recurErr == nil {
				fields = append(fields, recurFields...)
			}
		} else {
			if withValue {
				field := parseRequestField(typeOf.Field(i), valueOf.Field(i))
				fields = append(fields, field)
			} else {
				field := parseQueryField(typeOf.Field(i))
				fields = append(fields, field)
			}
		}
	}
	return fields, err
}

// ParseAndCheckSimpleForm parse and check one form field
func ParseAndCheckSimpleForm(c *gin.Context, field string, obj interface{}) (err error) {
	if err = ParseSimpleForm(c, field, obj); err == nil {
		err = CheckRequest(obj)
	}
	return
}

// ParseAndCheckFormData parse and check form data
func ParseAndCheckFormData(c *gin.Context, obj interface{}) (err error) {
	if err = c.MustBindWith(obj, globalFormBinding); err == nil {
		err = CheckRequest(obj)
	}
	return
}

// ParseAndCheckRequest parse and check request
func ParseAndCheckRequest(c *gin.Context, obj interface{}) (err error) {
	if err = ParseRequest(c, obj); err == nil {
		err = CheckRequest(obj)
	}
	return
}

// ParseAndCheckRequestForm parse and check request form
func ParseAndCheckRequestForm(c *gin.Context, key string, obj interface{}) (err error) {
	if err = ParseRequestForm(c, key, obj); err == nil {
		err = CheckRequest(obj)
	}
	return
}
